package cn.aaa.service.impl;

import cn.aaa.constants.CacheConstants;
import cn.aaa.constants.MQConstants;
import cn.aaa.constants.OrderStatus;
import cn.aaa.domain.DistanceResult;
import cn.aaa.domain.RuleVo;
import cn.aaa.exception.GlobalException;
import cn.aaa.openFeign.RuleOpenFeign;
import cn.aaa.pojo.bo.CreateOrderBo;
import cn.aaa.pojo.domain.GeoSearchResult;
import cn.aaa.pojo.domain.Order;
import cn.aaa.mapper.OrderMapper;
import cn.aaa.pojo.domain.RedisDriverSetting;
import cn.aaa.pojo.domain.vo.OrderVo;
import cn.aaa.result.JSONResult;
import cn.aaa.service.IOrderService;
import cn.aaa.template.TxMapTemplate;
import cn.aaa.utils.CodeGenerateUtils;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
import org.apache.rocketmq.client.consumer.PullResult;
import org.apache.rocketmq.client.consumer.PullStatus;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author axy
 * @since 2025-04-12
 */
@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {
    @Autowired
    private TxMapTemplate txMapTemplate;
    @Autowired
    private RuleOpenFeign ruleOpenfeign;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private DefaultMQPullConsumer pullConsumer;
    @Autowired
    private DefaultMQPullConsumer defaultMQPullConsumer;
    
    
    @Override
    public JSONResult createOrder(CreateOrderBo createOrderBo) {
        //第一步  下单之前判断是否已经有订单了
        Order one = super.getOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getCustomerId, StpUtil.getLoginIdAsLong())
                .in(Order::getStatus, Arrays.asList(OrderStatus.zero, OrderStatus.one, OrderStatus.two, OrderStatus.three)));
        if(one!=null){//已经下过订单了
            throw new GlobalException("你还有没有完成的订单");
        }
        //第二步  没有订单  构建订单 
        Order order = getOrder(createOrderBo);
        super.save(order);
        //找司机
        List<GeoSearchResult> geoSearchResults = searchGeo(CacheConstants.GEO,
                Double.valueOf(createOrderBo.getStartPlaceLongitude()),
                Double.valueOf(createOrderBo.getStartPlaceLatitude()),
                5.0, 10L);
        
        for (GeoSearchResult geoSearchResult : geoSearchResults) {
            //得到司机的id
            Object member = geoSearchResult.getMember();
            //从缓存里面取司机的配置 
            String key = String.format(CacheConstants.KEY_DRIVER_ONLINE, member.toString());
            RedisDriverSetting driverSetting = (RedisDriverSetting)redisTemplate.opsForValue().get(key);
            //判断司机的配置是否符合接单条件 是否为自动接单 是否在设置的范围内
            if(driverSetting.getAutoAccept()
                    && driverSetting.getOrderDistance()>=order.getExpectsMileage().intValue()
                    && driverSetting.getRangeDistance()>= 5){
                //构建司机显示信息
                OrderVo orderVo = buildOrderVo(order,geoSearchResult);
                String tags = MQConstants.TAG_ORDER2DRIVER + driverSetting.getId();
                SendResult sendResult = rocketMQTemplate.syncSend(MQConstants.TOPIC_ORDER2DRIVER + ":" + tags, orderVo);
                log.info("推送消息给司机： {}, 结果：{}",orderVo,sendResult);
            }
        }
        //将订单号返回给前端
        return JSONResult.success(order.getOrderNo());
    }
    private Order getOrder(CreateOrderBo createOrderBo) {
        Order order = new Order();
        //生成orderId
        order.setId(IdUtil.createSnowflake(0L,0L).nextId());
        //生成订单号
        order.setOrderNo(CodeGenerateUtils.generateUnionPaySn());
        order.setCreateTime(new Date());
        order.setCarPlate(createOrderBo.getCarPlate());
        order.setCarType(createOrderBo.getCarType());
        //获取乘客Id
        order.setCustomerId(StpUtil.getLoginIdAsLong());
        //从扩展参数中取出username phone  //todo 无法获取扩展参数 
        order.setCustomerName(StpUtil.getExtra("username").toString());
        order.setCustomerPhone(StpUtil.getExtra("phone").toString());
        //设置起始位置
        order.setStartPlace(createOrderBo.getStartPlace());
        order.setStartPlaceLongitude(createOrderBo.getStartPlaceLongitude());
        order.setStartPlaceLatitude(createOrderBo.getStartPlaceLatitude());
        //设置终点位置
        order.setEndPlace(createOrderBo.getEndPlace());
        order.setEndPlaceLongitude(createOrderBo.getEndPlaceLongitude());
        order.setEndPlaceLatiude(createOrderBo.getEndPlaceLatitude());
        //设置订单状态为待接单
        order.setStatus(OrderStatus.zero);
        //预计时长
        order.setExpectMinutes(createOrderBo.getExpectMinutes());
        // 计算距离 
//        order.setExpectsMileage(BigDecimal.valueOf(createOrderBo.getExpectsMileage())); //从前端拿到距离
        DistanceResult distanceResult = txMapTemplate.distanceMeasure(TxMapTemplate.DRIVING,
                Double.valueOf(createOrderBo.getStartPlaceLatitude()),
                Double.valueOf(createOrderBo.getStartPlaceLongitude()),
                Double.valueOf(createOrderBo.getEndPlaceLatitude()),
                Double.valueOf(createOrderBo.getEndPlaceLongitude()));
        order.setExpectsMileage(new BigDecimal(distanceResult.getDistance()));
        // 计算金额
//        order.setExpectsOrderAmount(BigDecimal.valueOf(0.0)); //从前端拿到金额
        RuleVo ruleVo = new RuleVo();
        ruleVo.setMileage(distanceResult.getDistance());
        JSONResult pricing = ruleOpenfeign.pricing(ruleVo);
        if(!pricing.isSuccess()){
            throw new GlobalException("服务器异常");
        }
        String amount = pricing.getData().toString();
        order.setExpectsOrderAmount(new BigDecimal(amount));
        return order;
    }
    /**
     * 坐标搜索
     * @param key ： 司机坐标的key
     * @param longitude ：中心点的经度
     * @param latitude ：中心点的维度
     * @param radius ：搜索的半径
     * @param limit ：匹配几个成员
     * @return ：命中的结果
     */
    public List<GeoSearchResult> searchGeo(String key,double longitude, double latitude,double radius, Long limit){
        //得到圆
        Circle circle = new Circle(new Point(longitude,latitude),new Distance(radius, Metrics.KILOMETERS));
        //配置我想要的结果
        RedisGeoCommands
                .GeoRadiusCommandArgs args = RedisGeoCommands
                .GeoRadiusCommandArgs
                .newGeoRadiusArgs();
        args.includeDistance()
                .includeCoordinates()
                .sortAscending()
                .limit(limit);
        //得到结果
        GeoResults<RedisGeoCommands.GeoLocation<Object>> geoResults = redisTemplate
                .opsForGeo()
                .radius(key, circle, args);
        List<GeoResult<RedisGeoCommands.GeoLocation<Object>>> content = geoResults.getContent();
        if(content.size() == 0){
            return null;
        }
        return content.stream().map(result ->
                new GeoSearchResult(
                        result.getDistance().getValue(),
                        result.getDistance().getUnit(),
                        result.getContent().getName(),
                        result.getContent().getPoint().getX(),
                        result.getContent().getPoint().getY()
                )
        ).collect(Collectors.toList());
    }
    
    /*
    * 取消订单
    * */
    @Override
    public JSONResult cancel(String orderNo) {
        Order one = super.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNo, orderNo));
        if(one != null){
            one.setStatus(OrderStatus.nine);
            one.setUpdateTime(new Date());
            super.updateById(one);
        }
        return JSONResult.success();
    }
    /*
    * 检查订单
    * */
    @Override
    public JSONResult checkOrder(String orderNo) {
        Order one = super.getOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getOrderNo, orderNo).in(Order::getStatus,
                        Arrays.asList(OrderStatus.one, OrderStatus.two, OrderStatus.three)));
        if(one != null){
            return JSONResult.success(one);
        }
        return null;
    }
    /*
    * 加载订单
    * */
    @Override
    public JSONResult inprocess() {
        long loginIdAsLong = StpUtil.getLoginIdAsLong();
        Order one = super.getOne(new LambdaQueryWrapper<Order>().eq(Order::getCustomerId, loginIdAsLong)
                .in(Order::getStatus, Arrays.asList(OrderStatus.zero, OrderStatus.one, OrderStatus.two, OrderStatus.three)));
        if(one != null){
            return JSONResult.success(one);
        }
        return null;
    }
    
    /*初始化返回对象*/
    private OrderVo buildOrderVo(Order order,GeoSearchResult geoSearchResult) {
        DistanceResult distanceResult = txMapTemplate.distanceMeasure(TxMapTemplate.DRIVING,
                Double.valueOf(geoSearchResult.getLat()),
                Double.valueOf(geoSearchResult.getLon()),
                Double.valueOf(order.getStartPlaceLatitude()),
                Double.valueOf(order.getStartPlaceLongitude()));
        //计算 司机到起点的距离
        OrderVo orderVo = new OrderVo();
        orderVo.setOrderNo(order.getOrderNo());
        orderVo.setTo(order.getEndPlace());
        orderVo.setFrom(order.getStartPlace());
        orderVo.setExpectsFee(order.getExpectsOrderAmount());
        orderVo.setMileage(order.getExpectsMileage());
        orderVo.setDistance(distanceResult.getDistance().doubleValue());
        orderVo.setFavourFee(order.getFavourAmount());
        return orderVo;
    }
    /*拉取订单*/
    @Override
    public JSONResult pullMQOrder() {
        //通过stpUtil拿到司机id
        long driverId = StpUtil.getLoginIdAsLong();
        ArrayList<OrderVo> list = new ArrayList<>();
        
        try {
            Set<MessageQueue> messageQueues = defaultMQPullConsumer.fetchSubscribeMessageQueues(MQConstants.TOPIC_ORDER2DRIVER);
            //循环消息队列
            for (MessageQueue messageQueue : messageQueues) {
                //偏移量  可以理解为索引index  记录当前已经消费的位置
                long offset = defaultMQPullConsumer.fetchConsumeOffset(messageQueue, true);
                //得到tag
                String tags = MQConstants.TAG_ORDER2DRIVER + driverId;
                //手动获取到结果
                PullResult pull = defaultMQPullConsumer.pull(messageQueue, tags, offset, 32);
                //判断是否拿到结果
                if(pull != null && pull.getPullStatus().equals(PullStatus.FOUND)){
                    List<MessageExt> messageExtList = pull.getMsgFoundList();
                    //判断  队列里面是否有数据
                    if(messageExtList == null || messageExtList.size() == 0)continue;
                    
                    for (MessageExt messageExt : messageExtList) {
                        //拿到消息内容
                        String message = new String(messageExt.getBody());
                        //转换为order对象
                        OrderVo order = JSON.parseObject(message, OrderVo.class);
                        list.add(order);
                    }
                }
                if(pull != null){
                    //重要：修改消息的消费位置，如果位置不后移，消息会一直被消费
                    defaultMQPullConsumer.updateConsumeOffset(messageQueue,pull.getNextBeginOffset());
                }
            }
        } catch (Exception e) {
            throw new GlobalException("订单拉取失败");
        }
        return JSONResult.success(list);
    }
}
